package jee.boot.data.permission.execute;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import jee.boot.common.basic.CustomSqlDao;
import jee.boot.common.basic.DataEntity;
import jee.boot.common.basic.JoinTable;
import jee.boot.entity.system.Filter;
import jee.boot.common.properties.ProjectConfig;
import jee.boot.common.remote.RemoteEndpoint;
import jee.boot.common.standard.AuthorityHandler;
import jee.boot.common.utils.HttpServletHolder;
import jee.boot.common.utils.MatcherCompilationHelper;
import jee.boot.common.utils.SpringContextHolder;
import jee.boot.common.utils.StringUtils;
import jee.boot.entity.auth.*;
import jee.boot.entity.auth.AuthConstant;
import jee.boot.entity.auth.standard.DataValue;
import jee.boot.entity.system.Operator;
import jee.boot.entity.system.TreeModel;
import jee.boot.entity.system.TreeNode;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class BasicExecute {
    protected AuthorityHandler authorityHandler;
    protected CustomSqlDao customSqlDao;
    protected RemoteEndpoint remoteEndpoint;
    protected ProjectConfig projectConfig;

    public BasicExecute() {
        this.projectConfig = SpringContextHolder.getApplicationContext().getBean(ProjectConfig.class);
        this.remoteEndpoint = SpringContextHolder.getApplicationContext().getBean(RemoteEndpoint.class);
        this.authorityHandler = SpringContextHolder.getApplicationContext().getBean(AuthorityHandler.class);
        this.customSqlDao = SpringContextHolder.getApplicationContext().getBean(CustomSqlDao.class);
    }

    /**
     * 获取当前权限用户
     *
     * @return
     */
    protected User getCurrentUser() {
        return (User) authorityHandler.getCurrentUser();
    }

    /**
     * 获取当前的request请求
     *
     * @return
     */
    protected HttpServletRequest getHttpServletRequest() {
        return HttpServletHolder.getHttpServletRequest();
    }

    /**
     * 配置有查询权限的列属性
     *
     * @param user
     * @param standardModel
     */
    protected void configColumnPermission(User user, StandardModel standardModel) {
        List<AuthEntity> columnList = mergeColumnPermission(user.getColumnList());
        standardModel.setHasViewList(Lists.newArrayList());
        if (columnList != null && !columnList.isEmpty()) {

            for (AuthEntity column : columnList) {
                ModelData modelData = ((Column) column.getEntity()).getModelData();
                // 查看当前查询是否有需要拦截的表
                if (!equalModelIgnoreSchema(modelData, standardModel)) {
                    continue;
                }
                standardModel.setColumnPermission(columnList);
                standardModel.getHasViewList().add((Column) column.getEntity());
            }
        }


    }

    /**
     * 配置有查询权限的列属性
     *
     * @param user
     * @param standardModel
     */
    protected void configCellSetPermission(User user, StandardModel standardModel) {
        List<AuthEntity> cellSetList = user.getCellSetList();
//        List<AuthEntity> cellSetList = mergeCellSetPermission(user.getCellSetList());
        if (cellSetList != null && !cellSetList.isEmpty()) {
            for (AuthEntity cellSet : cellSetList) {
                ModelData modelData = ((CellSet) cellSet.getEntity()).getModelData();
                // 查看当前查询是否有需要拦截的表
                if (!equalModelIgnoreSchema(modelData, standardModel)) {
                    continue;
                }
                standardModel.getCellSetPermission().add(cellSet);
            }
        }


    }

    /**
     * 配置有查询权限的列属性
     *
     * @param user
     * @param standardModel
     */
    protected void configRowSetPermission(User user, StandardModel standardModel) {
        List<AuthEntity> currentRequestRowSetPermission = getCurrentRequestRowSetPermission(user, standardModel);
        standardModel.setRowSetPermission(currentRequestRowSetPermission);
    }

    /**
     * 查询当前请求的行权限配置
     *
     * @param user
     * @param entity
     */
    protected List<AuthEntity> getCurrentRequestRowSetPermission(User user, DataEntity entity) {
        List<AuthEntity> rowSetList = user.getRowSetList();
        List<AuthEntity> retList = Lists.newArrayList();
        if (rowSetList != null && !rowSetList.isEmpty()) {
            for (AuthEntity rowSet : rowSetList) {
                ModelData modelData = ((RowSet) rowSet.getEntity()).getModelData();
                // 查看当前查询是否有需要拦截的表
                if (!equalModelIgnoreSchema(modelData, entity)) {
                    continue;
                }
                retList.add(rowSet);
            }
        }
        return retList;


    }

    /**
     * 合并单元格权限
     *
     * @param columnList
     * @return
     */
    protected List<AuthEntity> mergeColumnPermission(List<AuthEntity> columnList) {
        //这里的合并规则是按着model进行合并,即同一个model(model.tableName_model.tableAlias唯一)下只保留优先级高的配置
        //<model.tableName_model.tableAlias,AuthEntity>
        Map<String, AuthEntity> temp = Maps.newHashMap();
        if (columnList != null && !columnList.isEmpty()) {
            columnList.stream().forEach(rowEntity -> {
                Column column = (Column) rowEntity.getEntity();
                ModelData modelData = column.getModelData();
                String key = modelData.getModelTableName() + "_" + modelData.getTableAlias() + "_" + column.getCode();
                AuthEntity authEntity = temp.get(key);
                if (authEntity == null) {
                    // 第一次出现当前模型的配置,直接放进去
                    temp.put(key, rowEntity);
                } else {
                    //第2+次出现,则需要比较优先级了
                    Integer rowPriority = rowEntity.getOperationLevel().getPriority();
                    Integer tempPriority = authEntity.getOperationLevel().getPriority();
                    if (tempPriority < rowPriority) {
                        // 缓存中的优先级,小于循环行中的优先级,则替换
                        temp.put(key, rowEntity);
                    }
                }

            });
            if (!temp.isEmpty()) {
                return temp.values().stream().collect(Collectors.toList());
            }
        }
        return Lists.newArrayList();
    }

    /**
     * 检查当前模型是否匹配到当前的查询中的表,可以匹配from表,或者匹配join表
     *
     * @param modelData
     * @param entity
     * @return
     */
    protected boolean equalModelIgnoreSchema(ModelData modelData, DataEntity<?> entity) {
        String tableNameFromEntity = entity.getTableName();
        String tableNameFromModel = modelData.getModelTableName();
        // 匹配主查询表
        if (StringUtils.equals("a", modelData.getTableAlias()) && match(tableNameFromEntity, tableNameFromModel)) {
            return true;
        }
        // 匹配关联表
        List<JoinTable> joinTables = entity.joinTable();
        if (!joinTables.isEmpty()) {
            for (JoinTable joinTable : joinTables) {
                if (StringUtils.equals(modelData.getTableAlias(), joinTable.getAlias()) && match(joinTable.getTableName(), tableNameFromModel)) {
                    return true;
                }
            }

        }
        return false;
    }

    /**
     * 匹配模型表与程序查询的表是否匹配
     * <pre>
     *     match("dim_dev.st_user","st_user")=>true
     *     match("dim_dev/st_user","st_user")=>true
     *     match("dim_dev/st_user","ST_USER")=>true
     *     match("dim_dev/st_user","user")=>false
     * </pre>
     *
     * @param entityTable 数据表
     * @param modelTable  模型表
     * @return
     */
    protected boolean match(String entityTable, String modelTable) {
        if (StringUtils.equals(entityTable, modelTable)) {
            return true;
        }
        return StringUtils.endsWithIgnoreCase(entityTable, "/" + modelTable) || StringUtils.endsWithIgnoreCase(entityTable, "." + modelTable);
    }

    /**
     * 对查询返回的数据进行DataValue的permission包装设置
     *
     * @param rowData           查询返回的数据
     * @param rowSetPermission  行权限
     * @param cellSetPermission 单元格权限
     */
    protected Map<String, DataValue> getDataValuePermission(DataEntity rowData, List<AuthEntity> rowSetPermission, List<AuthEntity> cellSetPermission) {
        return getDataValuePermission(dataEntity2Map(rowData), rowSetPermission, cellSetPermission);
    }

    /**
     * 对查询返回的数据进行DataValue的permission包装设置
     *
     * @param rowData           查询返回的数据,map类型的行数据
     * @param rowSetPermission  行权限
     * @param cellSetPermission 单元格权限
     */
    protected Map<String, DataValue> getDataValuePermission(Map<String, Object> rowData, List<AuthEntity> rowSetPermission, List<AuthEntity> cellSetPermission) {
        Map<String, DataValue> map = Maps.newHashMap();
        // 行集权限
        AuthEntity defaultVisible = new AuthEntity(new OperationLevel(0, AuthConstant.OPERATION_LEVEL_VISIBLE));
        AuthEntity authEntityInRowSet = rowDataInRowSetPermission(rowData, rowSetPermission, defaultVisible);
        rowData.forEach((key, value) -> {
            String p;
            // 单元格集权限
            AuthEntity authEntityCellSet = rowDataInCellSetPermission(key, cellSetPermission, defaultVisible);
            if (authEntityInRowSet.getOperationLevel().getPriority() > authEntityCellSet.getOperationLevel().getPriority()) {
                p = authEntityInRowSet.getOperationLevel().getRuleKeyword();
            } else {
                p = authEntityCellSet.getOperationLevel().getRuleKeyword();
            }
            map.put(key, new DataValue(value, p));
        });
        return map;
    }

    /**
     * 当前行数据在cellSet中的匹配权限
     *
     * @param columnCode        列属性
     * @param cellSetPermission 单元格权限集合
     * @param defaultVisible    默认权限
     * @return 当为空时候返回只读权限
     */
    protected AuthEntity rowDataInCellSetPermission(String columnCode, List<AuthEntity> cellSetPermission, AuthEntity defaultVisible) {
        AuthEntity retAuthEntity = defaultVisible;
        for (AuthEntity authEntity : cellSetPermission) {
            if (StringUtils.equals(columnCode, ((CellSet) authEntity.getEntity()).getColumnData().getCode())) {
                // 设置更高的权限级别为ret权限
                if (authEntity.getOperationLevel().getPriority() > retAuthEntity.getOperationLevel().getPriority()) {
                    retAuthEntity = authEntity;
                }
            }
        }
        return retAuthEntity;
    }

    /**
     * 当前行数据在rowSet中的匹配权限
     *
     * @param rowData          行数据
     * @param rowSetPermission 行权限集合
     * @param defaultVisible   默认权限
     * @return 当为空时候返回只读权限
     */
    protected AuthEntity rowDataInRowSetPermission(Map<String, Object> rowData, List<AuthEntity> rowSetPermission, AuthEntity defaultVisible) {
        AuthEntity retAuthEntity = defaultVisible;
        if (rowSetPermission != null && !rowSetPermission.isEmpty()) {
            for (AuthEntity authEntity : rowSetPermission) {
                RowSet rowSet = (RowSet) authEntity.getEntity();
                List<RowSetItem> itemList = rowSet.getItemList();
                if (itemList != null && !itemList.isEmpty()) {
                    // 所有的规则必须全部满足,才能算是匹配到当前的rowSet中
                    boolean allMatch = true;
                    for (RowSetItem rowSetItem : itemList) {
                        // 规则定义: 操作符 eq: =
                        Operator operator = rowSetItem.getOperator();
                        if (StringUtils.isBlank(operator.getId())) {
                            continue;
                        }
                        // 规则定义: 表达式 eq: 张三
                        String formula = rowSetItem.getFormula();
                        // 规则定义: 维度 eq: name
                        Column dim = rowSetItem.getDim();
                        String dataDomainType = dim.getDataDomainType();
                        Object value = rowData.get(dim.getCode());
                        switch (dataDomainType) {
                            // 树节点
                            case AuthConstant.SYSTEM_DATA_DOMAIN_TYPE_TREE_NODE:
                                String treeModelId = dim.getDataDomain();
                                TreeModel treeModel = remoteEndpoint.executePost(projectConfig.getOpenSystemServer(),"/treeModel/get",new TreeModel(treeModelId),TreeModel.class);
                                String tableTreeSql = tableTreeSql(getCurrentUser(), rowSetItem, treeModel);
                                List<Map<String, Object>> list = customSqlDao.select(tableTreeSql);
                                if (list != null && !list.isEmpty()) {
                                    List<String> ids = list.stream().map(map -> (String) map.get("id")).collect(Collectors.toList());
                                    operator.setTechName("in");//树表的默认都是用in来检索
                                    // 这里需要检索当前树节点数据所挂载的父类是否在自己的权限范围之内
                                    if (rowData.containsKey("parent")) {
                                        DataEntity parent = (DataEntity) rowData.get("parent");
                                        if (!match(operator, StringUtils.join(ids, ","), parent.getId())) {
                                            allMatch = false;
                                            continue;
                                        }
                                    } else {
                                        allMatch = false;
                                        continue;
                                    }

                                } else {
                                    allMatch = false;
                                    continue;
                                }
                                break;
                            // 字典取值
                            case AuthConstant.SYSTEM_DATA_DOMAIN_TYPE_DICTIONARY:
                                // 主数据
                            case AuthConstant.SYSTEM_DATA_DOMAIN_TYPE_MASTER_DATA:
                                // 枚举字符串
                            case AuthConstant.SYSTEM_DATA_DOMAIN_TYPE_MENU_STRING:
                                //字典,主数据,枚举字符串,都采用普通的处理方式,包括 =,>,<,like,等基础操作
                                // 数据查询结果值: eq: 张三
                                if (!match(operator, formula, value)) {
                                    allMatch = false;
                                    continue;
                                }
                                break;
                        }


                    }
                    // 所有的item都匹配,则表示匹配上当前的rowSet规则
                    if (allMatch) {
                        // 设置更高的权限级别为ret权限
                        if (authEntity.getOperationLevel().getPriority() > retAuthEntity.getOperationLevel().getPriority()) {
                            retAuthEntity = authEntity;
                        }
                    }
                }
            }
        }
        return retAuthEntity;
    }

    /**
     * 用数据匹配公式的规则,返回是否匹配成功
     * eq: 公式: name like 'zhagnsan';数据: 张三 ;返回 false
     *
     * @param operator 操作符 like
     * @param formula  公式 'zhansan'
     * @param value    数据 '张三'
     * @return
     */
    protected boolean match(Operator operator, String formula, Object value) {
        /**
         * in
         * like
         * =
         * >
         * <
         * <>
         * not in
         * is null
         * is not null
         */
        String v = value == null ? null : value.toString();
        switch (operator.getTechName()) {
            case "=":
                return StringUtils.equals(formula, v);
            case ">":
                return new BigDecimal(v).compareTo(new BigDecimal(formula)) > 0;
            case ">=":
                return new BigDecimal(v).compareTo(new BigDecimal(formula)) >= 0;
            case "<":
                return new BigDecimal(v).compareTo(new BigDecimal(formula)) < 0;
            case "<=":
                return new BigDecimal(v).compareTo(new BigDecimal(formula)) <= 0;
            case "in":
                return Lists.newArrayList(StringUtils.split(formula, ",")).contains(v);
            case "not in":
                return !Lists.newArrayList(StringUtils.split(formula, ",")).contains(v);
            case "<>":
            case "!=":
                return !StringUtils.equals(v, formula);
            case "is null":
                return v == null;
            case "is not null":
                return v != null;

        }
        return false;
    }

    /**
     * 标准数据转换成Map数据,用于后面的通用的数据权限检查
     *
     * @param entity
     * @return
     */
    protected Map<String, Object> dataEntity2Map(DataEntity entity) {
        Map<String, Object> map = Maps.newHashMap();
        if (entity != null) {
            Map<String, Field> fieldAsMap = entity.getFieldAsMap();
            if (!fieldAsMap.isEmpty()) {
                fieldAsMap.forEach((fieldName, field) -> {
                    try {
                        field.setAccessible(true);
                        map.put(fieldName, field.get(entity));
                    } catch (Exception e) {
                        // 忽略这个不能发生的错误
                    }
                });
            }
        }
        return map;
    }

    /**
     * 获取树表的权限sql
     *
     * @return
     */
    protected String tableTreeSql(User user, RowSetItem rowSetItem, TreeModel treeModel) {
        // 从用户的session中取出表达式对应的文本值
        String formulaValue = MatcherCompilationHelper.simpleCompilation(user, rowSetItem.getFormula());
        // 单表树模型
        if (StringUtils.equals(treeModel.getModelCategory(), AuthConstant.TREE_MODEL_CATEGORY_SINGLE_TABLE)) {
            return singleTableTreeSql(formulaValue, treeModel);
        } else {
            //通用树模型
            return generalTableTreeSql(formulaValue, treeModel);
        }
    }


    /**
     * 通用树表sql
     *
     * @param formulaValue
     * @param treeModel
     * @return
     */
    private String generalTableTreeSql(String formulaValue, TreeModel treeModel) {
        StringBuilder sb = new StringBuilder();
        TreeNode treeNode = new TreeNode();
        treeNode.addFilter(new Filter("entityId", "=", formulaValue));
        treeNode.addFilter(new Filter("treeDefinition", "=", treeModel.getTreeDefinition().getId()));
        List<TreeNode> list = remoteEndpoint.executePost(projectConfig.getOpenSystemServer(),"/treeNode/all",treeNode,TreeNode.class);
        if (list != null && !list.isEmpty()) {
            TreeNode treeNode1 = list.get(0);
            sb.append("select st_entity_id as id from st_tree_node where id ='" + treeNode1.getParent().getId() + "' or(st_path like '" + treeNode1.getPath() + "%' and st_tree_id='" + treeModel.getTreeDefinition().getId() + "')");
        }
        return sb.toString();
    }

    /**
     * 单表树sql
     *
     * @param formulaValue
     * @param treeModel
     * @return
     */
    private String singleTableTreeSql(String formulaValue, TreeModel treeModel) {
        StringBuilder sb = new StringBuilder();
        // formulaValue为数组的情况
        if (StringUtils.contains(formulaValue, ",")) {
            String[] split = StringUtils.split(formulaValue, ",");
            String ids = "('" + StringUtils.join(split, "','") + "')";
            sb.append("select id from " + treeModel.getTreeTableName() + " where id in " + ids);
            for(String key :split){
                sb.append(" or parent_ids like '%," + key + ",%'");
            }
        } else {
            // formulaValue 为单个id的情况
            sb.append("select id from " + treeModel.getTreeTableName() + " where id='" + formulaValue + "' or parent_ids like '%," + formulaValue + ",%'");
        }
        return sb.toString();
    }

}
